#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>

#include <linux/uaccess.h>



struct device_test {
    dev_t dev_num;              // 设备号
    int major;                  // 主设备号
    int minor;                  // 次设备号
    struct cdev cdev_test;      // 字符设备

    struct class *class;        // 类
    struct device *device;      // 设备
    char kbuf[32];              // 缓存区buf 
};


struct device_test dev1;





static int cdev_open(struct inode *inode, struct file *file)
{
    // 设置私有数据
    file->private_data = &dev1;

    printk(KERN_EMERG "This is cdev_open\r\n");
    return 0;
}

static ssize_t cdev_write(struct file *file, const char __user *buff, size_t size, loff_t * off)
{

    struct device_test *test_dev = (struct device_test *)file->private_data;

    if (copy_from_user(test_dev->kbuf, buff, size) != 0)
    {
        printk(KERN_EMERG "copy_from_user error\r\n");
        return -1;
    }
    printk(KERN_EMERG "This is cdev_write\r\n");
    printk(KERN_EMERG "kbuf : %s\r\n", test_dev->kbuf);

    return 0;
}


static ssize_t cdev_read(struct file *file, char __user *buff, size_t size, loff_t *off)
{
    struct device_test *test_dev = (struct device_test *)file->private_data;

    if (copy_to_user(buff, test_dev->kbuf, strlen(test_dev->kbuf)) != 0)
    {
        printk(KERN_EMERG "copy_to_user error\r\n");
        return -1;
    }
    printk(KERN_EMERG "This is cdev_read\r\n");
    return 0;
}


static int cdev_release (struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "This is cdev_release\r\n");
    return 0;
}


struct file_operations cdev_fops = {
    .owner = THIS_MODULE,
    .open = cdev_open,
    .read = cdev_read,
    .write = cdev_write,
    .release = cdev_release,
};


static int __init chrdev_init(void)
{
    int ret ;

    ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "chrdev_test");
    if (ret < 0)
    {
        printk(KERN_EMERG "alloc chrdev is error\n");
        return -1;
    }
    printk(KERN_EMERG "alloc chrdev is ok\n");

    dev1.major = MAJOR(dev1.dev_num);
    dev1.minor = MINOR(dev1.dev_num);
    printk(KERN_EMERG "major = %d  | minor = %d\n", dev1.major, dev1.minor);


    dev1.cdev_test.owner = THIS_MODULE;
    cdev_init(&dev1.cdev_test, &cdev_fops);
    cdev_add(&dev1.cdev_test, dev1.dev_num, 1);

    dev1.class = class_create(THIS_MODULE, "test");
    dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "chr_test");

    return 0;
}


static void __exit chrdev_exit(void)
{
    device_destroy(dev1.class, dev1.dev_num);
    class_destroy(dev1.class);
    cdev_del(&dev1.cdev_test);
    unregister_chrdev_region(dev1.dev_num, 1);
}


module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("treasure1029");
